/*
 *  Slots.cpp
 *  vorpal
 *
 *  Created by rwebb on 30/05/08.
 *
 */

#include "Slots.h"
#include "Objects.h"
#include "VM.h"

namespace Vorpal{

Handle<Object> Slots::Get(Message key, VM *vm){
	if(key < arraySlots.size()){
		return arraySlots[key];
	}
	else{
		if(mapSlots.find(key) != mapSlots.end()){
			return mapSlots[key];
		}
		else{
			map< Message, Handle<Object> >::const_iterator delegate = mapSlots.find(msg_delegate);
			if(delegate != mapSlots.end()){
				if(delegate->second->HasSlot(key)){
					return delegate->second->Get(key, vm);
				}
				for(Message index = 0; delegate->second->HasSlot(index); ++index){
					Handle<Object> dest = delegate->second->Get(index, vm);
					if(dest->HasSlot(key)){
						return dest->Get(key, vm);
					}
				}
			}
		}
	}
	return vm->None();
}

void Slots::Set(Message key, Handle<Object> value){
	if(key < arraySlots.size()){
		arraySlots[key] = value;
	}
	else if(key == arraySlots.size()){ // expanding by more than one is trickier but possible by scanning mapSlots
		if(mapSlots.find(key) != mapSlots.end()){
			mapSlots.erase(key);
		}
		arraySlots.push_back(value);
		key += 1;
		while(mapSlots.find(key) != mapSlots.end()){
			arraySlots.push_back( mapSlots[key] );
			mapSlots.erase(key);
			key += 1;
		}				
	}
	else{
		mapSlots[key] = value;
	}
}
	
void Slots::AppendTo(Slots &array) const{
	loop(i, 0, arraySlots.size()){
		if(array.mapSlots.find(array.ArraySize()) != array.mapSlots.end()){
			array.mapSlots.erase(array.ArraySize());
		}
		array.arraySlots.push_back( arraySlots[i] ); 
		cout << "Add: " << arraySlots[i]->ToString() << endl;
	}	
}

void Slots::CopySlots(Slots &clone) const{
	loop(i, 0, arraySlots.size()){
		clone.Set(i, arraySlots[i]); // TODO: should we copy directly to the arraySlots?
	}
	for(map< Message, Handle<Object> >::const_iterator slot = mapSlots.begin(); slot != mapSlots.end(); ++slot){
		clone.Set(slot->first, slot->second);
	}
}
	
void Slots::ApplyToSlots(VM *vm, Handle<Code> code, Handle<Object> system, uint numArgs, Handle<Object> *args, bool namedSlots){
	loop(i, 0, arraySlots.size()){
		vm->Execute(code, system, Handle<Object>(arraySlots[i]), numArgs, args); 
	}
	if(namedSlots){
		for(map< Message, Handle<Object> >::const_iterator slot = mapSlots.begin(); slot != mapSlots.end(); ++slot){
			vm->Execute(code, system, Handle<Object>(slot->second), numArgs, args); 
		}	
	}
}

#define WRITE(os, x) os.write((const char *)&x, sizeof(x));
#define READ(is, x) is.read((char *)&x, sizeof(x));
	
void Slots::ArchiveSlots(std::ostream &output, VM *vm, SerialMemory &previous){
	uint arrayNum = arraySlots.size();
	WRITE(output, arrayNum);
	loop(i, 0, arrayNum){
		arraySlots[i]->Serialize(output, vm, &previous);
	}
	for(map< Message, Handle<Object> >::const_iterator slot = mapSlots.begin(); slot != mapSlots.end(); ++slot){
		Message msg = slot->first;
		WRITE(output, msg);
		slot->second->Serialize(output, vm, &previous);
	}
	Message end = 0;
	WRITE(output, end);
}

void Slots::UnArchiveSlots(std::istream &input, VM *vm, SerialMemory &previous){
	uint arrayNum;
	READ(input, arrayNum);
	arraySlots.resize(arrayNum);
	loop(i, 0, arrayNum){
		arraySlots[i] = Object::Unserialize(input, vm, &previous);
	}
	mapSlots.clear();
	while(true){
		Message msg;
		READ(input, msg);
		if(msg == 0){ break; }
		mapSlots[msg] = Object::Unserialize(input, vm, &previous);
	}
}


}
